/**
 * jsDate: Date Utility v0.2 (Beta)
 * http://code.google.com/p/date-util-js/
 * 
 * Copyright (c) 2011, Helcon Mabesa - http://www.hmabesa.com/
 * Dual licensed under the MIT and GPL licenses.
 * 
 */

(function(){
	
	var jsDate = function(context) {
		return new jsDate.fn.init(context);
	};
	
	jsDate.fn = jsDate.prototype = {
		init: function(context) {
			var obj = validator.check(context);
			if (obj) {
				this[0] = obj;
				this.context = context;
				this.length = 1;
				return this;
			} else {
				helper.throwErr("[DateUtilJS] Invalid date constructor");
				return null;
			}
		},
		extend: function(prop) {
			var i; for (i in prop) {
				this[i] = prop[i];
			}
		},
		setConstr: function(curr,val) {
			var ret = jsDate(curr);
			ret.orig = this;
			return ret;
		},
		
		orig: false,
		push: [].push,
		sort: [].sort,
		splice: [].splice
	};
	
	jsDate.fn.extend({
		format: function(context) {
			context = context || false;
			var JD = this;
			var date = JD[0];
			if (!date) { helper.throwErr("[DateUtilJS] Invalid date format"); return null; }

			var obj = helper.dateObj(date);
			
			var formatter = function(str) {
				var replace = {
					"${mm}": (obj.month+1),
					"${month}": helper.getMonthStr(obj.month),
					"${month:short}": helper.getMonthStr(obj.month,"short"),
					"${day}": helper.getDayStr(obj.day),
					"${day:short}": helper.getDayStr(obj.day,"short"),
					"${yr}": obj.year,
					"${dd}": (obj.date<10)?("0"+obj.date):obj.date,
					"${ddTH}": helper.oldEnglish(obj.date),
					"${hr}": ((obj.hour>12)?obj.hour-12:obj.hour),
					"${hr:mil}": obj.hour,
					"${hr:mil:2}": (obj.hour<10)?("0"+obj.hour):obj.hour,
					"${min}": (obj.minutes<10)?("0"+obj.minutes):obj.minutes,
					"${sec}": (obj.seconds<10)?("0"+obj.seconds):obj.seconds,
					"${ampm}": obj.ampm,
					"${AMPM}": obj.ampm.toUpperCase(),
					"${AmPm}": obj.ampm.charAt(0).toUpperCase()+obj.ampm.charAt(1)
				};

				var i; for (i in replace) {
					var idx = str.indexOf(i);
					while (idx>-1) {
						str = str.replace(i,replace[i]);
						idx = str.indexOf(i);
					}
				}
				return str;
			};

			if (!context) { 
				helper.console("[DateUtilJS] No string format requested, returning default string"); 
				return date.toDateString();
			} else {
				switch(typeof context) {
					case "string":
						context = formatter(context);
						return context;
					case "function":
						context(obj, formatter);
						break;
				}

			}
		},
		
		set: function(context) {
			var obj = validator.check(context);
			if (!obj) { helper.throwErr("[DateUtilJS] Invalid date constructor"); }
			return this.setConstr(obj);
		},
		
		yesterday: function() {
			var d = setYesterday(this.context), ret = this.set(d);
			return ret;
		},
		
		tomorrow: function() {
			var d = setTomorrow(this.context), ret = this.set(d);
			return ret;
		},
		
		ahead: function(n) {
			if (isNaN(n)) { helper.throwErr("[DateUtilJS] Not a number: daysAhead("+n+")"); return null; }
			var d = helper.addToDate(this.context,Math.abs(n)), ret = this.set(d);
			return ret;
		},
		
		prior: function(n) {
			if (isNaN(n)) { helper.throwErr("[DateUtilJS] Not a number: daysAhead("+n+")"); return null; }
			var d = helper.addToDate(this.context,parseInt("-"+(Math.abs(n)),10)), ret = this.set(d);
			return ret;
		}
		
	});
	
	jsDate.fn.init.prototype = jsDate.fn;
	
	// get "today"
	jsDate.today = function() {
		return jsDate(helper.dateToday());
	};
	
	// get "yesterday"
	jsDate.yesterday = function() {
		return jsDate(setYesterday());
	};

	// get "tomorrow"
	jsDate.tomorrow = function() {
		return jsDate(setTomorrow());
	};
	
	function setTomorrow(date) {
		var d = helper.addToDate((date) ? date : helper.dateToday(),1);
		return d;
	}
	
	function setYesterday(date) {
		var d = helper.addToDate((date) ? date : helper.dateToday(),-1);
		return d;
	}
	
	var validator = {
		check: function(context) {
			var VD = this, val = false;
			
			switch(typeof context) {
				case "string":
					val = VD.isDateObj(context);
					val = (val) ? val : VD.isSelector(context);
					break;
				case "object": 
					val = VD.isDateObj(context);
					break;
			}
			return val;
		},
		isDateObj: function(val) {
			if (!val) { return false; }
			if (isNaN(Date.parse(val))) { return false; }
			return (new Date(val));
		}
	};
	
	var helper = {
		console: function(str) {
			if (typeof window.console==="object" && typeof console.log==="function") { console.log(str); }
		},
		throwErr: function(str) {
			str = "Exeption: " + str ;
			throw str;
		},
		dateToday: function() {
			return (new Date());
		},
		addToDate: function(date,n) {
			date = new Date(date.setDate(date.getDate()+n));
			return date;
		},
		dateObj: function(date) {
			var obj = {
				// date
				month: date.getUTCMonth(),
				day: date.getDay(),
				year: date.getFullYear(),
				"date": date.getDate(),
				// time
				hour: date.getHours(),
				minutes: date.getMinutes(),
				seconds: date.getSeconds(),
				ampm: ((date.getHours()<12)?'am':'pm'),
				fullStr: date.toString()
			};
			return obj;
		},
		oldEnglish: function(num) {
			num = num.toString();
			var tenth = parseInt(num.slice(num.length-2),10);
			var lastDigit = parseInt(num.charAt(num.length-1),10);
			
			if (tenth > 10 && tenth < 20) {
				num+="th";
			} else {
				switch (lastDigit) {
					case 1: num+="st";break;
					case 2: num+="nd";break;
					case 3: num+="rd";break;
					default: num+="th";break;
				}
			}
			return num;
		},
		getMonthStr: function(n,type) {
			type = type || "full"; //type is optional
			var arr = ["January","February","March","April","May","June","July","August","September","October","November","December"],
				month = arr[n];
			if (type==="short") { month = month.substr(0,3); }
			return month;
		},
		getDayStr: function(n,type) {
			type = type || "full";
			var arr = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
				day = arr[n];
			if (type==="short") { day = day.substr(0,(n===4) ? 4 : 3); }
			return day;
		},
		normalizeArgs: function(args) {
			var obj = {};
			if (args.length===1) {
				var arg = args[0];
				if ((typeof arg==="object" && !isArray(arg)) || typeof arg==="string") {
					obj.str = arg;
				}
			} else {
				var key = args[0], val = args[1];
				if (typeof key==="string" && typeof val==="string") {
					obj[key] = val;
				}
			}
			return obj;
		}
	};
	
	function isArray(val) {
		val = val || false;
		return Object.prototype.toString.call(val) === "[object Array]"; 
	}
	
	window.jDate = window.jsDate = jsDate; // window scope
	
}());
